/*************************************************
 * Copyright (C), 2009-2020, Kong.
 * File name: syn_analysis.c
 * Author: Kong       Version: 1.0       Date: 2009.6.6
 * Description:  //词法、语法分析模块
 * Others:
 * Function List:  // 主要函数列表，每条记录应包括函数名及功能简要说明
 * History:
*************************************************/

#include "global.h"
#include "err_.h"

/**
 * get_first_token:
 * 读取缓冲区内的第一个token
 *************************************************
 * 参数：
 * buf		: 输入缓冲区
 * 返回值		： 没有token： NULL
 * 		  有token： 新申请的指向token的指针
 */
static char *get_first_token(char **buf)
{
	if (buf == NULL) {
		err_quit("%s: buffer error", PROJECT_NAME);
		return NULL;
	}

	char *buf_ptr;
	buf_ptr = *buf;
	while (*buf_ptr == ' ') {
		buf_ptr++;
	}//跳过头部空白字符
	*buf = buf_ptr;
	while (*buf_ptr != ' ' && *buf_ptr != '&' &&
	       *buf_ptr != '>' && *buf_ptr != '<' && *buf_ptr != '\0') {
		buf_ptr++;
	}

	if (buf_ptr == *buf) {
		return NULL;
	}
	char *token = calloc(buf_ptr - *buf + 1, sizeof(char));
	strncpy(token, *buf, buf_ptr - *buf);
	//*(token + (buf_ptr - *buf)) = '\0';
	*buf = buf_ptr;
	#ifdef DEBUG
	//printf("Get Token: %s\n",token);
	#endif

	return token;
}

/**
 * get_token:
 * 读取缓冲区内的下一个token
 *************************************************
 * 参数：
 * buf		: 输入缓冲区
 * 返回值		： 没有token： NULL
 * 		  还有token： 新申请的指向token的指针
 */
static char *get_token(char **buf)
{
	if (*buf == NULL) {
		err_quit("%s: buffer error", PROJECT_NAME);
		return NULL;
	}

	char *token = NULL;
	char *buf_ptr;
	int flag = 0;

	buf_ptr = *buf;
	while (*buf_ptr == ' ') {
		buf_ptr++;
	}//跳过头部空白字符
	*buf = buf_ptr;
	//处理第一个字符
	if (*buf_ptr == '&' || *buf_ptr == '>' || *buf_ptr == '<') {
		token = calloc(2, sizeof(char));
		*token = *buf_ptr;
		(*buf)++;
		return token;
	}
	//如果是',则期望后面仍有'匹配
	if (*buf_ptr == '\'') {
		flag = 1;
		buf_ptr++;
		*buf = buf_ptr;
		while (*buf_ptr != '\'' && *buf_ptr != '\0') {
			//*(token_ptr++) = *(buf_ptr++); 
			buf_ptr++;
		}
	}
	//录入字符
	while (*buf_ptr != ' ' && *buf_ptr != '&' && *buf_ptr != '\'' &&
	       *buf_ptr != '>' && *buf_ptr != '<' && *buf_ptr != '\0') {
		//*(token_ptr++) = *(buf_ptr++); 
		buf_ptr++;
	}

	if (buf_ptr == *buf) {
		return NULL;
	}
	token = calloc(buf_ptr - *buf + 1, sizeof(char));
	strncpy(token, *buf, buf_ptr - *buf);
	//*(token + (buf_ptr - *buf)) = '\0';
	*buf = buf_ptr;
	if (flag && *buf_ptr == '\'') {
		(*buf)++;
	}
	#ifdef DEBUG
	//printf("Get Token: %s\n",token);
	#endif

	return token;;
}

/**
 * is_more_token:
 * 查看缓冲区内是否还有token
 *************************************************
 * 参数：
 * buf		: 输入缓冲区
 * 返回值		： 没有token：  0
 * 		  还有token：  1
 */
static int is_more_token(char *const buf)
{
	/*char token[MAX_LINE];                 
	   memset(token, 0, MAX_LINE);

	   get_token(buf_ptr, token);
	   if ( strcmp(token, "") != 0) {
	   return 0;
	   }

	   return 1;
	 */
	char *buf_ptr = buf;
	while (*buf_ptr == ' ') {
		buf_ptr++;
	}			//跳过头部空白字符

	return (*buf_ptr == '\0') ? 0 : 1;
}

/**
 * syn_analysis:
 * 语法分析函数，调用词法分析器函数完成词法分析，对输入命令
 * 进行语法分析，并完成简单的语法树
 *************************************************
 * 参数：
 * buf		: 输入缓冲区
 * cmd		: 保存命令数组
 * arguments	: 保存参数的二位数组 
 * flags	: 保存保留子设置标志的位图
 * 返回值		： 成功：  0
 * 		  失败： -1
 */
int syn_analysis(char *buf, char **cmd, char *arguments[], int *flags)
{
	if ((buf == NULL) || (cmd == NULL) || (flags == NULL)) {
		err_msg("buffer error");
		return -1;
	}

	char *buf_ptr = buf;
	//char token[MAX_LINE];
	char *token = NULL;

	*cmd = get_first_token(&buf_ptr);
	if (*cmd == NULL) {
		token = get_token(&buf_ptr);
		if (token != NULL) {
			err_msg("%s: syntex error on unexpected %s",
				PROJECT_NAME, token);
			free(token);
		}
		return -1;	//没有命令
	}
	arguments[3] = *cmd;
	SET_CMD(*flags);
        #ifdef DEBUG
	//printf("after SET_CMD, flags: %d\t, buf: %s\n",*flags, buf_ptr);
	//printf("after SET_CMD, cmd: %s, reserved_words[EXIT_INDEX]: %s\n", 
	//      cmd, reserved_words[EXIT_INDEX]);
        #endif

	//命令分析
	//case exit
	if (strcmp(*cmd, reserved_words[EXIT_INDEX]) == 0) {
		if (is_more_token(buf_ptr)) {
			err_msg("%s: %s: has too many arguments",
				PROJECT_NAME, reserved_words[EXIT_INDEX]);
			return -1;
		}
		SET_EXIT(*flags);
		return 0;
	}
	//case cd
	if (strcmp(*cmd, reserved_words[CD_INDEX]) == 0) {
		token = get_token(&buf_ptr);
		if (token != NULL) {
			if (is_more_token(buf_ptr)) {
				err_msg("%s: %s: has too many arguments",
					PROJECT_NAME,
					reserved_words[CD_INDEX]);
				free(token);
				return -1;
			}
			//arguments[0] = malloc(sizeof(char) * (strlen(token) + 1));
			//strcpy(arguments[0], token);
			arguments[4] = token;
			SET_CD(*flags);
			return 0;
		}
		err_msg("%s: %s: need one argument",
			PROJECT_NAME, reserved_words[CD_INDEX]);
		return -1;
	}
	//case chgpmt
	if (strcmp(*cmd, reserved_words[CHGPRMT_INDEX]) == 0) {
		token = get_token(&buf_ptr);
		if (token != NULL) {
			if (is_more_token(buf_ptr)) {
				err_msg("%s: %s: has too many arguments",
					PROJECT_NAME,
					reserved_words[CHGPRMT_INDEX]);
				free(token);
				return -1;
			}
			//arguments[0] = malloc(sizeof(char) * (strlen(token) + 1));
			//strcpy(arguments[0], token);
			arguments[4] = token;
			SET_CHGPRMT(*flags);
			return 0;
		}
		err_msg("%s: %s: need one argument",
			PROJECT_NAME, reserved_words[CHGPRMT_INDEX]);
		return -1;
	}
	//case fg
	if (strcmp(*cmd, reserved_words[RUN_FRNTGRND_INDEX]) == 0) {
		token = get_token(&buf_ptr);
		if (token != NULL) {
			if (is_more_token(buf_ptr)) {
				err_msg("%s: %s: has too many arguments",
					PROJECT_NAME,
					reserved_words
					[RUN_FRNTGRND_INDEX]);
				free(token);
				//free(*cmd);
				return -1;
			}
			//有一个参数
			//arguments[0] = malloc(sizeof(char) * (strlen(token) + 1));
			//strcpy(arguments[0], token);
			arguments[4] = token;
		}
		//没有参数
		SET_RUN_FRNTGRND(*flags);
		return 0;
	}
	
	//参数分析
	token = get_token(&buf_ptr);
	char **arg_ptr = (arguments + 4);
	while (token != NULL) {
		//case <
		//期望后面一个参数
		if (strcmp(token, reserved_words[INPUT_REDIRECTION_INDEX])
		    == 0) {
			if (!is_more_token(buf_ptr)) {
				err_msg("%s: %s: need one argument",
					PROJECT_NAME,
					reserved_words
					[INPUT_REDIRECTION_INDEX]);
				//free(*cmd);
				return -1;
			}
			//获取该参数
			token = get_token(&buf_ptr);
			//压入参数
			//检查参数合法性
			//is_legal(token);
			arguments[0] = token;
			token = get_token(&buf_ptr);
			SET_INPUT_REDIREC(*flags);
			continue;
		}
		//case >
		//期望后面有一个参数
		//后期扩展标准出错输出
		if (strcmp(token, reserved_words[OUTPUT_REDIRECTION_INDEX])
		    == 0) {
			if (!is_more_token(buf_ptr)) {
				err_msg("%s: %s: need one argument",
					PROJECT_NAME,
					reserved_words
					[OUTPUT_REDIRECTION_INDEX]);
				//free(*cmd);
				return -1;
			}
			//获取该参数
			token = get_token(&buf_ptr);
			//压入参数
			//检查参数合法性
			//is_legal(token);
			arguments[1] = token;
			token = get_token(&buf_ptr);
			SET_OUTPUT_REDIREC(*flags);
			continue;
		}
		//case &
		//期望已至行尾
		if (strcmp(token, reserved_words[RUN_BCKGRND_INDEX]) == 0) {
			if (is_more_token(buf_ptr)) {
				err_msg("%s: syntex error on %s",
					PROJECT_NAME,
					reserved_words[RUN_BCKGRND_INDEX]);
				free(token);
				//free(*cmd);
				return -1;
			}
			SET_RUN_BCKGRND(*flags);
			return 0;
		} else {
			*arg_ptr = token;
			arg_ptr++;
		}
		token = NULL;
		token = get_token(&buf_ptr);
	}//end while
	
	return 0;
}
